﻿using OPCAutomation;
using OPCPlatformCore.Exceptions;
using OPCPlatformCore.Interfaces;
using OPCPlatformCore.Managers;
using OPCPlatformCore.Models.OPCServerModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace OPCPlatformCore.Implementations
{
    public class OPCServerDataProvider : IOPCServerDataReader, IOPCServerDataWriter
    {
        OPCServerManager serverManager;
        public OPCServerDataProvider(string connectionString)
        {
            serverManager = new OPCServerManager(connectionString);
        }
        #region 异步读取数据,此处因为使用了Task,可能造成性能的降低
        public OPCServerMachine GetData(OPCServerMachine oPCServerMachine)
        {
            oPCServerMachine.SetReadSuccess(false);
            Task.Run(() => ReadData(oPCServerMachine));
            int waitCount = 0;
            while (!oPCServerMachine.ReadSuccess)
            {
                //等待10s,如果没有正确读取到数据,则抛出异常
                if (waitCount == 100)
                {
                    throw new OPCServerReadException(oPCServerMachine.MachineName);
                }
                Thread.Sleep(100);//此处不使用Task,Delay是因为此方法可能造成线程切换,即损失效率,也可能造成程序运行错误
                waitCount++;
            }
            serverManager.CloseOPC();
            return oPCServerMachine;
        }

        public OPCServerMachine GetData(string machineName)
        {
            OPCServerMachine currentMachine = serverManager.GetMachineByMachineName(machineName);
            currentMachine.TagInfos.ForEach(t => t.NeedRead = true);
            return GetData(currentMachine);
        }

        public OPCServerMachine GetData(string machineName, List<string> tagNames)
        {
            OPCServerMachine currentMachine = serverManager.GetMachineByMachineName(machineName);
            currentMachine.TagInfos.ForEach(t => { if (tagNames.Contains(t.FriendlyName)) { t.NeedRead = true; } else { t.NeedRead = false; } });
            return GetData(currentMachine);
        }
        /// <summary>
        /// 读取数据
        /// </summary>
        /// <param name="oPCServerMachine"></param>
        private void ReadData(OPCServerMachine oPCServerMachine)
        {
            try
            {
                //特别注意,OPCServer与OPCGroup必须位于同一个线程,否则特别容易造成程序进入死循环
                OPCGroup group = serverManager.GetGroupByMachineName(oPCServerMachine.MachineName);

                List<OPCServerTagInfo> readTags = oPCServerMachine.TagInfos.Where(t => t.NeedRead).ToList();
                int tagCount = readTags.Count;
                int[] serverHandles = new int[tagCount + 1];
                serverHandles[0] = 0;
                int serverIndex = 1;
                for (int i = 1; i <= group.OPCItems.Count; i++)
                {
                    OPCItem item = group.OPCItems.Item(i);
                    if (readTags.All(t => t.ClientHandle != item.ClientHandle))
                    {
                        continue;
                    }
                    serverHandles[serverIndex] = item.ServerHandle;
                    serverIndex++;
                }
                Array arraySh = (Array)serverHandles;
                Random random = new Random();
                int transactionId = random.Next(1, 10);
                Array errors;
                int cancelId;
                //特别注意,以下方法虽然命名为Async但实际上还是运行在当前线程中,所以不能使用While的方式来等待,否则程序会陷入死循环
                group.AsyncRead(tagCount, ref arraySh, out errors, transactionId, out cancelId);
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion

        #region 异步写入数据
        public bool WriteData(OPCServerMachine oPCServerMachine)
        {
            oPCServerMachine.SetWriteSuccess(false);
            Task.Run(() => UpdateData(oPCServerMachine));
            int waitCount = 0;
            while (!oPCServerMachine.WriteSuccess)
            {
                //等待10s,如果没有正确读取到数据,则抛出异常
                if (waitCount == 100)
                {
                    throw new OPCServerWriteException(oPCServerMachine.MachineName);
                }
                Thread.Sleep(100);//此处不使用Task,Delay是因为此方法可能造成线程切换,即损失效率,也可能造成程序运行错误
                waitCount++;
            }
            serverManager.CloseOPC();
            return true;
        }

        public bool WriteData(string machineName, List<KeyValuePair<string, object>> nameValues)
        {

            OPCServerMachine currentMachine = serverManager.GetMachineByMachineName(machineName);

            foreach (var item in currentMachine.TagInfos)
            {
                if (nameValues.Any(t => t.Key == item.FriendlyName))
                {
                    item.SetValue(nameValues.First(t => t.Key == item.FriendlyName).Value.ToString());
                }
            }
            return WriteData(currentMachine);
        }
        private void UpdateData(OPCServerMachine oPCServerMachine)
        {
            try
            {
                //特别注意,OPCServer与OPCGroup必须位于同一个线程,否则特别容易造成程序进入死循环
                OPCGroup group = serverManager.GetGroupByMachineName(oPCServerMachine.MachineName);

                List<OPCServerTagInfo> writeTags = oPCServerMachine.TagInfos.Where(t => t.NeedWrite).ToList();
                int tagCount = writeTags.Count;
                int[] serverHandles = new int[tagCount + 1];
                serverHandles[0] = 0;
                int serverIndex = 1;
                for (int i = 1; i <= group.OPCItems.Count; i++)
                {
                    OPCItem item = group.OPCItems.Item(i);
                    if (writeTags.All(t => t.ClientHandle != item.ClientHandle))
                    {
                        continue;
                    }
                    serverHandles[serverIndex] = item.ServerHandle;
                    serverIndex++;
                }
                Array arraySh = (Array)serverHandles;
                Random random = new Random();
                int transactionId = random.Next(1, 10);
                Array errors;
                int cancelId;
                //准备写入的数据
                object[] writeTemp = new object[writeTags.Count + 1];
                writeTemp[0] = "";
                for (int i = 0; i < writeTags.Count; i++)
                {
                    writeTemp[i + 1] = writeTags[i].Value;
                }
                Array writeValue = (Array)writeTemp;
                //特别注意,以下方法虽然命名为Async但实际上还是运行在当前线程中,所以不能使用While的方式来等待,否则程序会陷入死循环
                group.AsyncWrite(tagCount, ref arraySh, ref writeValue, out errors, transactionId, out cancelId);
            }
            catch (Exception)
            {
                throw;
            }
        }
        #endregion

        #region 注册机台
        public void RegisterMachine(OPCServerMachine oPCServerMachine)
        {
            serverManager.RegisterMachine(oPCServerMachine);
        }
        #endregion
    }
}
